package jwcb.admin.server.map.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.github.benmanes.caffeine.cache.Cache;
import jakarta.annotation.PostConstruct;
import jwcb.admin.api.map.constants.MapConstants;
import jwcb.admin.api.map.domain.dto.CityLocationReqDto;
import jwcb.admin.api.map.domain.dto.PlaceSearchReqDto;
import jwcb.admin.server.dictionary.service.ISysArgumentService;
import jwcb.admin.server.map.domain.dto.SysRegionDTO;
import jwcb.admin.server.map.domain.dto.qq.*;
import jwcb.admin.server.map.domain.entity.SysRegion;
import jwcb.admin.server.map.mapper.MapMapper;
import jwcb.admin.server.map.service.IMapProvider;
import jwcb.admin.server.map.service.IMapService;
import jwcb.javapjframework.common.cache.utils.CacheUtil;
import jwcb.javapjframework.common.core.utils.BeanCopyUtil;
import jwcb.javapjframework.common.core.utils.PageUtil;
import jwcb.javapjframework.common.domain.domain.dto.BasePageDTO;
import jwcb.javapjframework.common.redis.RedisService.service.RedisService;
import jwcb.javapjframework.common.redis.RedisService.service.RedissonLockService;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class MapServiceImpl implements IMapService {


    private static final Logger log = LoggerFactory.getLogger(MapServiceImpl.class);
    @Autowired
    private MapMapper mapMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private Cache<String,Object> caffeineCache;


    @Autowired
    private RedissonLockService redissonLockService;


    @Autowired
    private IMapProvider<QQSuggestSearchDTO> iMapProvider;

    @Autowired
    private ISysArgumentService sysArgumentService;

    /**
     * 在服务启动的时候 预热城市列表缓存
     */
    @PostConstruct
    public void initCityMap() {

        List<SysRegion> sysRegions = mapMapper.selectAllRegin();
        loadCityInfo(sysRegions);
        loadCityPinyinInfo(sysRegions);
//        loadHotCityList(sysRegions);
    }


    /**
     * 加载数据库到二级缓存
     */
    private List<SysRegionDTO> loadCityInfo(List<SysRegion> sysRegions) {
        List<SysRegionDTO> result = new ArrayList<>();
        for (SysRegion sysRegion : sysRegions) {
            if(MapConstants.CITY_LEVEL.equals(sysRegion.getLevel())) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        CacheUtil.setL2Cache(redisService,MapConstants.CACHE_MAP_CITY_KEY,result,caffeineCache);
        return result;
    }

    /**
     * 初始化A-Z城市归类信息
     * @return 归类信息
     */
    private Map<String,List<SysRegionDTO>> loadCityPinyinInfo(List<SysRegion> sysRegions) {
        Map<String,List<SysRegionDTO>> result = new HashMap<>();
        for(SysRegion sysRegion : sysRegions) {
            if(MapConstants.CITY_LEVEL.equals(sysRegion.getLevel())) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                String firstChar = sysRegion.getPinyin().toUpperCase().substring(0,1);
                if(result.containsKey(firstChar)) {
                    result.get(firstChar).add(sysRegionDTO);
                }else {
                    List<SysRegionDTO> list = new ArrayList<>();
                    list.add(sysRegionDTO);
                    result.put(firstChar,list);
                }
            }
        }

        CacheUtil.setL2Cache(redisService,MapConstants.CACHE_MAP_CITY_PINYIN_KEY,result,caffeineCache);
        return result;
    }


    /**
     * 加载热门城市列表
     * @param sysRegions 热门城市列表
     * @return
     */
    private List<SysRegionDTO> loadHotCityList(List<SysRegion> sysRegions) {
        String hotCityIds = sysArgumentService.getByConfigKey(MapConstants.MAP_HOT_CITY_CONFIG_KEY).getValue();
        List<Long> idList = new ArrayList<>();
        for(String num : hotCityIds.split(",")) {
            idList.add(Long.parseLong(num));
        }
        List<SysRegion> hotCityList = sysRegions.stream()
                .filter(region -> idList.contains(region.getId())).toList();
        List<SysRegionDTO> sysRegionDTOS = BeanCopyUtil.copyListProperties(hotCityList, SysRegionDTO::new);
        CacheUtil.setL2Cache(redisService,MapConstants.CACHE_MAP_HOT_CITY_KEY,sysRegionDTOS,caffeineCache);
        return sysRegionDTOS;
    }


    /**
     * 城市列表查询V1版本
     * @return 城市列表信息
     */
    public List<SysRegionDTO> getCityListV1() {
        List<SysRegionDTO> result = new ArrayList<>();
        List<SysRegion> sysRegions = mapMapper.selectAllRegin();
        for (SysRegion sysRegion : sysRegions) {
            if(MapConstants.CITY_LEVEL.equals(sysRegion.getLevel())) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        return result;
    }


    /**
     * 城市列表查询 - 单redis方案
     * @return 城市列表信息
     */
    public List<SysRegionDTO> getCityListV2() {
        List<SysRegionDTO> result = new ArrayList<>();
        List<SysRegionDTO> cache = redisService.getCacheObject(MapConstants.CACHE_MAP_CITY_KEY,
                new TypeReference<List<SysRegionDTO>>() {});
        if(cache != null) {
            return cache;
        }
        List<SysRegion> sysRegions = mapMapper.selectAllRegin();
        for (SysRegion sysRegion : sysRegions) {
            if(MapConstants.CITY_LEVEL.equals(sysRegion.getLevel())) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        redisService.setCacheObject(MapConstants.CACHE_MAP_CITY_KEY,result);
        return result;
    }


    /**
     * 城市列表查询 - 二级缓存方案 + 缓存预热
     * @return 城市列表信息
     */
    public List<SysRegionDTO> getCityListV3() {
        List<SysRegionDTO> result = new ArrayList<>();
        List<SysRegionDTO> cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_KEY, new TypeReference<List<SysRegionDTO>>() {}, caffeineCache);
        if(cache != null) {
            return cache;
        }
        List<SysRegion> sysRegions = mapMapper.selectAllRegin();
        for (SysRegion sysRegion : sysRegions) {
            if(MapConstants.CITY_LEVEL.equals(sysRegion.getLevel())) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        CacheUtil.setL2Cache(redisService,MapConstants.CACHE_MAP_CITY_KEY,result,caffeineCache);
        return result;
    }


    /**
     * 城市列表查询 - 加分布式锁 + 二级缓存版本 + 缓存预热
     * @return 城市列表信息
     */
    @Override
    public List<SysRegionDTO> getCityList() {
        List<SysRegionDTO> cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_KEY, new TypeReference<List<SysRegionDTO>>() {}, caffeineCache);
        if(cache != null) {
            return cache;
        }
        RLock lock = null;
        final String lockKey = MapConstants.CACHE_MAP_CITY_LOAD_LOCK_KEY + MapConstants.CACHE_MAP_CITY_KEY;
        try {
            lock = redissonLockService.acquireNoWait(lockKey,3000);
            if(lock != null) {
                log.info("获取锁成功");
                // 二次验证
                cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_KEY, new TypeReference<List<SysRegionDTO>>() {}, caffeineCache);
                if(cache != null) {
                    return cache;
                }
                List<SysRegion> sysRegions = mapMapper.selectAllRegin();

                return loadCityInfo(sysRegions);
            }else {
                log.info("获取锁失败");
                return new ArrayList<>();
            }
        }finally {
            // 确保锁释放
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 城市拼音归类查询
     * @return 城市字母与城市列表的哈希
     */
    @Override
    public Map<String, List<SysRegionDTO>> getCityPyList() {
        Map<String, List<SysRegionDTO>> cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_PINYIN_KEY, new TypeReference<Map<String, List<SysRegionDTO>>>() {
        }, caffeineCache);
        if(cache != null) {
            return cache;
        }
        RLock lock = null;
        final String lockKey = MapConstants.CACHE_MAP_CITY_PINYIN_LOAD_LOCK_KEY + MapConstants.CACHE_MAP_CITY_PINYIN_KEY;
        try {
            lock = redissonLockService.acquireNoWait(lockKey,3000);
            if(lock != null) {
                log.info("获取锁成功");
                // 二次验证
                cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_PINYIN_KEY, new TypeReference<Map<String, List<SysRegionDTO>>>() {
                }, caffeineCache);
                if(cache != null) {
                    return cache;
                }
                List<SysRegion> sysRegions = mapMapper.selectAllRegin();
                return loadCityPinyinInfo(sysRegions);
            }else {
                log.info("获取锁失败");
                return Map.of();
            }
        }finally {
            // 确保锁释放
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 根据父级区域ID获取子集区域列表
     * @param parentId 父级区域id
     * @return 子集区域列表
     */
    @Override
    public List<SysRegionDTO> getRegionChildrenList(Long parentId) {
        String key = MapConstants.CACHE_MAP_CITY_CHILDREN_KEY + parentId;
        List<SysRegionDTO> cache = CacheUtil.getL2Cache(redisService, key, new TypeReference<List<SysRegionDTO>>() {
        }, caffeineCache);
        if(cache != null) {
            return cache;
        }
        List<SysRegion> sysRegions = mapMapper.selectChildrenRegin(parentId);
        List<SysRegionDTO> sysRegionDTOS = BeanCopyUtil.copyListProperties(sysRegions, SysRegionDTO::new);
        CacheUtil.setL2Cache(redisService,key,sysRegionDTOS,caffeineCache,MapConstants.CACHE_MAP_CITY_CHILDREN_EXP, TimeUnit.MINUTES);
        return sysRegionDTOS;
    }


    /**
     * 获取热门城市列表
     * @return 热门城市列表
     */
    @Override
    public List<SysRegionDTO> getCityHotList() {
        List<SysRegionDTO> hotCityList = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_HOT_CITY_KEY,
                new TypeReference<List<SysRegionDTO>>() {},caffeineCache);
        if(hotCityList != null) {
            return hotCityList;
        }
        String hotCityIds = sysArgumentService.getByConfigKey(MapConstants.MAP_HOT_CITY_CONFIG_KEY).getValue();
        List<Long> idList = new ArrayList<>();
        for(String num : hotCityIds.split(",")) {
            idList.add(Long.parseLong(num));
        }
        List<SysRegion> sysRegions = mapMapper.selectBatchIds(idList);
        List<SysRegionDTO> sysRegionDTOS = BeanCopyUtil.copyListProperties(sysRegions, SysRegionDTO::new);
        CacheUtil.setL2Cache( redisService,MapConstants.CACHE_MAP_HOT_CITY_KEY,sysRegionDTOS,caffeineCache);
        return sysRegionDTOS;
    }


    /**
     * 根据地点搜索
     * @param placeSearchReqDto 搜索条件
     * @return 搜索结果
     */
    @Override
    public BasePageDTO<SearchPoiDtTO> searchSuggestOnMap(PlaceSearchReqDto placeSearchReqDto) {
        QQSuggestSearchDTO qqSuggestSearchDTO = new QQSuggestSearchDTO();
        qqSuggestSearchDTO.setPageIndex(placeSearchReqDto.getPageNo());
        qqSuggestSearchDTO.setPageSize(placeSearchReqDto.getPageSize());
        qqSuggestSearchDTO.setId(String.valueOf(placeSearchReqDto.getId()));
        qqSuggestSearchDTO.setKeyword(placeSearchReqDto.getKeyWord());
        PoiListDTO poiListDTO = iMapProvider.searchMapPlaceByRegion(qqSuggestSearchDTO);

        return assembleResult(placeSearchReqDto, poiListDTO);
    }


    /**
     * 根据经纬度定位城市
     * @param cityLocationReqDto 定位信息
     * @return 城市信息
     */
    @Override
    public RegionCityDTO getCityLocation(CityLocationReqDto cityLocationReqDto) {
        RegionCityDTO regionCityDTO = new RegionCityDTO();
        LocationDTO locationDTO = new LocationDTO();
        locationDTO.setLat(cityLocationReqDto.getLat());
        locationDTO.setLng(cityLocationReqDto.getLng());
        GeoResultDto geoResultDto = iMapProvider.getMapDistrictByLonLat(locationDTO);
        if(geoResultDto != null && geoResultDto.getResult() != null && geoResultDto.getResult().getAd_info() != null) {
            regionCityDTO.setAddress(geoResultDto.getResult().getAddress());
            String city = geoResultDto.getResult().getAd_info().getCity();
            List<SysRegionDTO> cache = CacheUtil.getL2Cache(redisService, MapConstants.CACHE_MAP_CITY_KEY,
                    new TypeReference<List<SysRegionDTO>>() {
                    }, caffeineCache);
            if(cache == null) {
                return regionCityDTO;
            }
            for(SysRegionDTO sysRegionDTO : cache) {
                if(sysRegionDTO.getFullName().equals(city)) {
                    BeanCopyUtil.copyProperties(sysRegionDTO,regionCityDTO);
                    return regionCityDTO;
                }
            }
        }
        return regionCityDTO;
    }


    /**
     * 根据地图服务返回结果 封装 返回结果
     * @param placeSearchReqDto 请求参数
     * @param poiListDTO 地图服务返回结果
     * @return 封装结果
     */
    private  BasePageDTO<SearchPoiDtTO> assembleResult(PlaceSearchReqDto placeSearchReqDto, PoiListDTO poiListDTO) {
        List<PoiDto> poiDtoList = poiListDTO.getData();
        BasePageDTO<SearchPoiDtTO> result = new BasePageDTO<>();
        result.setTotals(poiListDTO.getCount());
        result.setTotalPages(PageUtil.getTotalPages(result.getTotals(), placeSearchReqDto.getPageSize()));
        List<SearchPoiDtTO> pageRes = new ArrayList<>();
        for(PoiDto poiDto : poiDtoList) {
            SearchPoiDtTO searchPoiDtTO = new SearchPoiDtTO();
            searchPoiDtTO.setTitle(poiDto.getTitle());
            searchPoiDtTO.setAddress(poiDto.getAddress());
            searchPoiDtTO.setLatitude(poiDto.getLocation().getLat());
            searchPoiDtTO.setLongitude(poiDto.getLocation().getLng());
            pageRes.add(searchPoiDtTO);
        }
        result.setList(pageRes);
        return result;
    }
}
